package com.dong.auth.security;


import com.dong.auth.security.authentication.AuthenticationManager;
import com.dong.auth.security.authentication.AuthenticationProvider;
import com.dong.auth.security.authentication.DaoAuthenticationProvider;
import com.dong.auth.security.authentication.ProviderManager;
import com.dong.auth.security.crypto.PasswordEncoder;
import com.dong.auth.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.dong.auth.security.filter.AuthorizationInterceptorFilter;
import com.dong.auth.filter.LoginFilter;
import com.dong.auth.filter.LogoutFilter;
import com.dong.auth.security.filter.Oauth2Filter;
import com.dong.auth.security.matcher.AntPathRequestMatcher;
import com.dong.auth.security.matcher.LoginRequestMatcher;
import com.dong.auth.security.userdetails.SecurityUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.boot.web.servlet.filter.OrderedFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * 应用安全模块自动配置类
 *
 * @author zhaodc
 * @date 2021/10/14 9:41
 **/
@Configuration(proxyBeanMethods = false)
public class DongSecurityConfiguration {

    private final static String DONG_SECURITY_FILTER = "dongSecurityFilter" ;


    @Bean(name = DONG_SECURITY_FILTER)
    public Filter dongSecurityFilterChain(){
        List<DongSecurityFilterChain> filterChains = new ArrayList<>();
        // 默认的拦截路径: /**
        List<Filter> filters = new ArrayList<>();
        filters.add(new AuthorizationInterceptorFilter());
        filters.add(new Oauth2Filter());
        DongSecurityFilterChain defaultSecurityFilterChain = new DefaultDongSecurityFilterChain(new AntPathRequestMatcher(),filters);

        //装载过滤器链实例
        filterChains.add(0,defaultSecurityFilterChain);
        filterChains.add(1,DongSecurityFilterChainBuilder.createEmptyForUrl("/static/**"));

        //静态资源过滤器
        return  new DongSecurityFilterChainProxy(filterChains);
    }

    @Bean
    public DelegatingFilterProxyRegistrationBean dongSecurityFilter(){
        DelegatingFilterProxyRegistrationBean securityFilter = new DelegatingFilterProxyRegistrationBean(DONG_SECURITY_FILTER);
        //设置安全过滤器在ApplicationFilterChain中的顺序
        securityFilter.setOrder(OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100);
        //设置拦截请求类型
        HashSet<DispatcherType> dispatcherTypes = new HashSet<>(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
        EnumSet<DispatcherType> typeEnumSet = dispatcherTypes.stream()
                .map(type -> DispatcherType.valueOf(type.name()))
                .collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));

        securityFilter.setDispatcherTypes(typeEnumSet);

        return securityFilter ;
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider daoAuthenticationProvider(ApplicationContext applicationContext){
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setSecurityUserService(getBeanOrNull(applicationContext, SecurityUserService.class));
        authenticationProvider.setPasswordEncoder(getBeanOrNull(applicationContext,PasswordEncoder.class));
        return authenticationProvider ;
    }

    @Bean
    public AuthenticationManager authenticationManager(List<AuthenticationProvider> authenticationProviders){

        return new ProviderManager(authenticationProviders);
    }

    private <T> T getBeanOrNull(ApplicationContext applicationContext,Class<T> type) {
        if (applicationContext == null){
            return null;
        }
        String[] beanNames = applicationContext.getBeanNamesForType(type);
        if (beanNames.length != 1) {
            return null;
        }
        return applicationContext.getBean(beanNames[0], type);
    }
}
